package com.salton123.ast

import kastree.ast.Node
import kastree.ast.Visitor
import java.io.File
import java.nio.charset.Charset


object FunctionManager {
    fun getktAst(filename: String): KtAstProp {
        val content = File(filename).readText(Charset.defaultCharset()).trimIndent()
        val file = kastree.ast.psi.Parser.parseFile(content)
        val mFuncList = mutableListOf<HashMap<String, Any>>()
        val ktAsProp = KtAstProp()

//        println(imp)
        ktAsProp.importList = file.imports
        ktAsProp.funcList = mFuncList
        file.decls.forEachIndexed { index, decl ->
            if (decl is Node.Decl.Structured) {
                val fileName = decl.name
                ktAsProp.clzName = fileName
//                val parents = decl.parents
//                println(parents)
                val members = decl.members
                members.forEach { func ->
                    val mFuncMap = hashMapOf<String, Any>()
                    mFuncMap["fileName"] = fileName
                    if (func is Node.Decl.Func) {    //解析方法
                        val funcName = func.name
                        funcName?.let { mFuncMap.put("funcName", it) }
                        val funcParams = func.params
                        val mParamDatas = mutableListOf<Any>()
                        funcParams.forEachIndexed { index, param ->
                            val paramName = param.name
                            val mParamMap = hashMapOf<String, Any>()
                            mParamMap["name"] = paramName
                            val datas = mutableListOf<String>()
                            Visitor.visit(param.type!!) { v, parent ->
                                when (v) {
                                    is Node.TypeRef.Simple.Piece -> {
                                        datas.add(v.name)
                                    }
                                    else -> {
//                                        println(v)
                                    }
                                }
                            }
                            mParamMap["paramType"] = datas
                            mParamDatas.add(mParamMap)
                        }
                        mFuncMap["params"] = mParamDatas
                        mFuncList.add(mFuncMap)
                    }
                }
            }
        }
        return ktAsProp
    }

    private fun getImportList(imports: List<Node.Import>): MutableList<String> {
        return imports.map { getImportStr(it) }.toMutableList()
    }

    private fun getImportStr(importNode: Node.Import): String {
        return importNode.names.reduce { a, b ->
            StringBuffer().append(a).append(".").append(b).toString()
        }
    }

    fun getCallApis(saveProp: SaveProp, funcs: MutableList<HashMap<String, Any>>): String {
        val textSb = StringBuffer()
        val clzLine = "\tdef %s(self%s):\n" +
                "\t\timport %s\n" +
                "\t\tapi_cmd = %s\n" +
                "\t\tself.driver.send_command(api_cmd)\n\n"

        funcs.forEach {
            val funName = it["funcName"]
            var paramSb = StringBuffer()
            var paramCallSb = StringBuffer()
            if (it["params"] is MutableList<*>) {
                val params = it["params"] as MutableList<*>
                if (params.isNotEmpty()) {
                    paramSb.append(",")
                }
                params.forEachIndexed { index, param ->
                    if (param is HashMap<*, *>) {
                        val name = param["name"]
                        paramCallSb.append("$name($name).")
                        paramSb.append("${name},")
                    }
                }
            }
            val line =
                clzLine.format(
                    funName,
                    splitDot(paramSb),
                    "apis.${getFileName(saveProp.fileName)}_builder",
                    "apis.${getFileName(saveProp.fileName)}_builder" +
                            ".${captureName(funName as String)}ApiBuilder()" +
                            ".${if (splitDot(paramCallSb).isEmpty()) "" else "${splitDot(paramCallSb)}."}build()"
                )
            textSb.append(line)
        }
        return textSb.toString()
    }

    fun splitDot(rawStr: CharSequence): CharSequence {
        if (rawStr.isEmpty()) {
            return rawStr
        }
        try {
            return rawStr.subSequence(0, rawStr.length - 1)
        } catch (ex: Exception) {
            ex.printStackTrace()
            println("splitDot:$rawStr")
        }
        return rawStr
    }

    fun getFileName(filePath: String): String {
        return filePath.replace(".kt", "")
    }

    fun saveToFile(saveProp: SaveProp, ktAstProp: KtAstProp) {
        val funcs: MutableList<HashMap<String, Any>> = ktAstProp.funcList
        val textSb = StringBuffer()
        textSb.append("# encoding: utf-8\n#sdk version: ${saveProp.version}\n\n")
        textSb.append("from sdktest_module.utils.api_base_builder import ApiBaseBuilder, parameter\n\n")
        val clzLine =
            "class %sApiBuilder(ApiBaseBuilder):\n" +
                    "\tapi_name = \"%s\"" +
                    "\n" +
                    "\n" +
                    "%s"
        funcs.forEach {
            val funName = it["funcName"]
            val apiName = "${saveProp.packageName}.$funName"
            var paramSb = StringBuffer()
            if (it["params"] is MutableList<*>) {
                val params = it["params"] as MutableList<*>
                params.forEachIndexed { index, param ->
                    if (param is HashMap<*, *>) {
                        val paramType = param["paramType"]
                        if (paramType is MutableList<*>) {
                            val paramTypeT = paramType as MutableList<String>
                            var paramStr = if (paramTypeT.isEmpty()) "" else paramTypeT[0]

                            ktAstProp.importList.forEach { importNodeList ->
                                importNodeList.names.forEach { name ->
                                    if (name == paramStr) {
                                        paramStr = getImportStr(importNodeList)
                                    }
                                }
                            }

                            val name = param["name"]
                            val methods =
                                "\t@parameter(${index + 1},\"${externalPatchPackageName(convertBaseTypeToJava(paramStr))}\")\n" +
                                        "\tdef $name(self, $name):\n" +
                                        "\t\treturn self\n" +
                                        "\n"
                            paramSb.append(methods)
                        }
                    }
                }
            }

            val line =
                clzLine.format(
                    captureName(funName as String),
                    apiName,
                    paramSb
                )
            textSb.append(line)
            File(saveProp.savePath + saveProp.fileName.replace(".kt", "_builder.py")).writeText(textSb.toString())
        }
    }

    private fun externalPatchPackageName(param: String): String {
        when (param) {
            "ChatRoomCallback" -> {
                return "tv.athena.live.api.channel.$param"
            }
            "IDisableCallback",
            "ISensitiveWordsCallback",
            "IReceiveMessageCallback"
            -> {
                return "tv.athena.live.api.chatroom.$param"
            }
            "ILivePublishQualityListener" -> {
                return "tv.athena.live.api.broadcast.$param"
            }
            "LinkMicUnicastListener" -> {
                return "tv.athena.live.api.link_mic.$param"
            }
            "AuditType",
            "OnReportListener"
            -> {
                return "tv.athena.live.api.report.ReportApi.$param"
            }
            "RoomInfoV2Listener" -> {
                return "tv.athena.live.api.roominfov2.$param"
            }
            "VideoPositionWrapper" -> {
                return "tv.athena.live.api.videoarea.$param"
            }
            "AudienceLineStreamInfoListener",
            "MicInfoListener" -> {
                return "tv.athena.live.api.wath.$param"
            }
            else -> {
                return param
            }
        }
    }

    /**
     * 由于auto test sdk还不支持kt反射，只能转为java
     */
    private fun convertBaseTypeToJava(baseType: String): String {
        return when (baseType) {
            "Long" -> {
                "long"
            }
            "Int" -> {
                "int"
            }
            "LongArray" -> {
                "long[]"
            }
            "Boolean" -> {
                "boolean"
            }
            "Any" -> {
                "Object"
            }
            "kotlin.collections.HashMap"->{
                "java.util.HashMap"
            }
            "IntArray" -> {
                "int[]"
            }
            else -> baseType
        }
    }

    fun captureName(name: String): String {
        val ame = name.substring(0, 1).toUpperCase() + name.substring(1)
        return ame
    }
}